package com.google.code.yargon.ui.component;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

import org.lwjgl.input.Mouse;
import org.newdawn.slick.util.Log;

import com.google.code.yargon.Yargon;
import com.google.code.yargon.ui.component.Component.Hotspot;

import static com.google.code.yargon.util.Constants.*;

/**
 * 
 * @author Mika Myllynen
 *
 */
public class ComponentManager {

	private LinkedList<Component> components = null;
	private HashMap<String, Integer> mapping = null;
	
	public ComponentManager() {
		components = new LinkedList<Component>();
		mapping = new HashMap<String, Integer>();
	}	
	
	/**
	 * Register a new component with ComponentManager. Also buffer it (render it to buffer ready to be rendered on screen) if it is a BufferedComponent.
	 * 
	 * @param name component name as a String
	 * @param c Component object
	 */
	public void registerComponent(String name, Component c, int position) {
		
		if(position == FIRST) {
			components.addFirst(c);	
			if(mapping.size() == 0) {
				mapping.put(name, 0);
			} 
			else { 
				for(Map.Entry<String, Integer> entry : mapping.entrySet()) {
					int pos = entry.getValue();
					mapping.put(entry.getKey(), ++pos);
				}
				mapping.put(name,  0);
			}
		} 
		
		else if(position == LAST) {
			components.addLast(c);
			mapping.put(name, components.size());
		} 
		
		else {
			Log.error("Cannot register component with unknown position, must be FIRST or LAST");
		}
	
		if(c instanceof BufferedComponent) {
			((BufferedComponent) c).buffer();
		}
	}
	
	/**
	 * Return the named component
	 * 
	 * @param name
	 * @return
	 */
	private Component getComponent(String name) {
		int pos = mapping.get(name);
		return components.get(pos);
	}
	
	/**
	 * Refresh named component
	 * 
	 * @param name component name as a string
	 */
	public void refreshComponent(String name) {
		Component c = getComponent(name); 
		if(c instanceof BufferedComponent) {
			((BufferedComponent) c).buffer();
		}
	}
	
	/**
	 * Set components display x-coordinate
	 * 
	 * @param name
	 * @param x
	 */
	public void setX(String name, int x) {
		getComponent(name).setX(x);
	}

	/**
	 * Set components display y-coordinate
	 * 
	 * @param name
	 * @param x
	 */
	public void setY(String name, int y) {
		getComponent(name).setY(y);
	}
	
	/**
	 * Set components alpha channel value
	 * 
	 * @param name
	 * @param x
	 */
	public void setAlpha(String name, float alpha) {
		getComponent(name).setAlpha(alpha);
	}
	
	/**
	 * Display all components on screen in the registration order
	 * 
	 */
	public void displayAll() {
		for(Component c : components) {
			c.display();
		}
		/*
		for(Map.Entry<String, Component> entry : components.entrySet()) {
			Component c = entry.getValue();		
			c.display();
		} */
	}
	
	/**
	 * Update Component states and handle mouse input
	 * 
	 */
	public void update() {
								
		// We need to iterate in reverse order since on top components should get mouse actions first
		for(int i = components.size() - 1; i >= 0; i--) {
			Component c = components.get(i);
			c.update();
			
			if(c.isUpdated()) {
				if(c instanceof BufferedComponent) {
					((BufferedComponent)c).buffer();
				} else {
					c.render();
				}
				c.setUpdated(false);
			}
						
			// Current absolute mouse coordinates
			int cx = Mouse.getX();
			int cy = Mouse.getY();
			
			// Current relative mouse coordinates (relative to component x,y)
			int rx = cx - c.getX();
			int ry = cy - c.getY();
			
			// Mouse button states
			int eventButton = Mouse.getEventButton();
			boolean buttonDown = Mouse.isButtonDown(0);
			
			// Check what needs to be done if the mouse is inside this component or the component is moving
			if(c.isMoving() || (rx >= 0 && rx <= c.getWidth() && ry >= 0 && ry <= c.getHeight())) {
										
				// First iterate through every hot spot
				for(Map.Entry<String, Hotspot> h : c.getHotspots().entrySet()) {
					Hotspot hs = h.getValue();
				
					// Do we have a click on the hot spot? 
					if(buttonDown) {
						if(rx >= hs.getX() && rx <= hs.getX() + hs.getW() && 
						   ry >= hs.getY() && ry <= hs.getY() + hs.getH() &&
						   ! hs.getActive()) {
							c.onClick(h.getKey());
							c.setClicked(true);
							hs.setActive(true);
						} 
					} 					
					else {
						hs.setActive(false);
						c.setClicked(false);
					} 
				}
				
				// Next, if click was not handled yet and button is down: move component 
				if(! c.isClicked() && c.isMovable()) {
					if(eventButton != -1) {
						if(buttonDown && ! c.isMoving()) {
							c.setMoving(true);
							c.setMouseDownX(rx); 
							c.setMouseDownY(ry); 
						} else if(! buttonDown && c.isMoving()) {
							c.setMoving(false);
							
							moveComponent(c, cx - c.getMouseDownX(), cy - c.getMouseDownY());
							displayAll();
						}
					}
					if(buttonDown && c.isMoving()) {
						moveComponent(c, cx - c.getMouseDownX(), cy - c.getMouseDownY());
						displayAll();
					} 
				}
				
				// No further inspection for mouse clicks, one component already got focus
				break;
			}
		}
	}
	
	private void moveComponent(Component c, int x, int y) {
		if(x >= c.sl && x + c.getWidth() - Yargon.getInstance().getWindowWidth() <= c.sr) {
			c.setX(x);
		} 
		else if(x < c.sl) {
			c.setX(c.sl);
		} 
		else if((x + c.getWidth()) - (Yargon.getInstance().getWindowWidth() + c.sr) > c.sr) {
			c.setX(Yargon.getInstance().getWindowWidth() + c.sr - c.getWidth());
		}
		
		if(y >= c.sb && y + c.getHeight() - Yargon.getInstance().getWindowHeight() <= c.st) {
			c.setY(y);
		} 
		else if(y < c.sb) {
			c.setY(c.sb);
		} 
		else if((y + c.getHeight()) - (Yargon.getInstance().getWindowHeight() + c.st) > c.st) {
			c.setY(Yargon.getInstance().getWindowHeight() + c.st - c.getHeight());
		}
	}
}
